﻿#region Imports
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using CommonUtilities.Commands;
using CommonUtilities.Extensions;
using CommonUtilities.Info;
using CommonUtilities.Util;
using OneStopProgrammingMatrix.Cache;
using OneStopProgrammingMatrix.Common;
using OneStopProgrammingMatrix.Models;
using OneStopProgrammingMatrix.Properties;
#endregion Imports

namespace OneStopProgrammingMatrix.ViewModels
{
    public class OSPConfig : BaseModel
    {
        #region Properties
        private bool isSortAscending;
        public bool IsSortAscending
        {
            get { return isSortAscending; }
            set
            {
                isSortAscending = value;
                RaisePropertyChanged("IsSortAscending");
            }
        }

        private bool isDefaultFirst;
        public bool IsDefaultFirst
        {
            get { return isDefaultFirst; }
            set
            {
                isDefaultFirst = value;
                RaisePropertyChanged("IsDefaultFirst");
            }
        }

        private bool isGroupingAllowed;
        public bool IsGroupingAllowed
        {
            get { return isGroupingAllowed; }
            set
            {
                isGroupingAllowed = value;
                RaisePropertyChanged("IsGroupingAllowed");
            }
        }

        private bool isEmptyAllowed;
        public bool IsEmptyAllowed
        {
            get { return isEmptyAllowed; }
            set
            {
                isEmptyAllowed = value;
                RaisePropertyChanged("IsEmptyAllowed");
            }
        }

        private bool isAddNewVisible = false;
        public bool IsAddNewVisible
        {
            get { return isAddNewVisible; }
            set { isAddNewVisible = value; RaisePropertyChanged("IsAddNewVisible"); }
        }
        #endregion Properties
    }

    public enum eUpdateMode
    {
        Add, Edit
    }

    public class MatrixViewModel : BaseViewModel
    {
        #region Declarations
        Dictionary<string, bool> openStates;
        bool isLoadComplete = false;
        #endregion Declarations

        #region Properties
        private string title;
        public string Title
        {
            get { return title; }
            set { title = value; RaisePropertyChanged("Title"); }
        }

        private ObservableCollection<MatrixItem> allLinks;
        public ObservableCollection<MatrixItem> AllLinks
        {
            get { return allLinks; }
            set
            {
                allLinks = value;
                RaisePropertyChanged("AllLinks");

                AllLinksGrouped = CreateItemGroups(allLinks);
            }
        }

        private ObservableCollection<MatrixItemGrouped> allLinksGrouped;
        public ObservableCollection<MatrixItemGrouped> AllLinksGrouped
        {
            get { return allLinksGrouped; }
            set { allLinksGrouped = value; RaisePropertyChanged("AllLinksGrouped"); }
        }

        private bool isSortAscending;
        public bool IsSortAscending
        {
            get { return isSortAscending; }
            set
            {
                isSortAscending = value;
                RaisePropertyChanged("IsSortAscending");

                RefreshData();
            }
        }

        private bool isDefaultFirst;
        public bool IsDefaultFirst
        {
            get { return isDefaultFirst; }
            set
            {
                isDefaultFirst = value;
                RaisePropertyChanged("IsDefaultFirst");

                RefreshData();
            }
        }

        private bool isGroupingAllowed;
        public bool IsGroupingAllowed
        {
            get { return isGroupingAllowed; }
            set
            {
                isGroupingAllowed = value;
                RaisePropertyChanged("IsGroupingAllowed");

                RefreshData();
            }
        }

        private bool isEmptyAllowed;
        public bool IsEmptyAllowed
        {
            get { return isEmptyAllowed; }
            set
            {
                isEmptyAllowed = value;
                RaisePropertyChanged("IsEmptyAllowed");

                RefreshData();
            }
        }

        private bool isExpanded;
        public bool IsExpanded
        {
            get { return isExpanded; }
            set
            {
                isExpanded = value;
                RaisePropertyChanged("IsExpanded");

                AllLinksGrouped = AllLinksGrouped._ForEach(grp => grp.IsGroupOpen = isExpanded).ToObservableCollection();
            }
        }

        private bool isAddNewVisible = false;
        public bool IsAddNewVisible
        {
            get { return isAddNewVisible; }
            set { isAddNewVisible = value; RaisePropertyChanged("IsAddNewVisible"); }
        }

        public RelayCommand<string> OpenLinkCommand { get; private set; }
        public RelayCommand<string> SearchCommand { get; private set; }
        public RelayCommand<eUpdateMode> OpenUpdaterCommand { get; private set; }
        #endregion

        #region Constructors
        public MatrixViewModel()
        {
            System.Windows.Forms.MessageBox.Show("RKD Save current view settings / Add app icons", "Dev !nfo");

            InitializeCommands();
            IsExpanded = IsGroupingAllowed = true;

            Title = AppInfo.AssemblyTitle;

            RefreshConfig();
            DataCache.DBMatrixItems = LoadData();

            isLoadComplete = true;
            RefreshData();
        }
        #endregion Constructors

        #region Methods
        private void InitializeCommands()
        {
            OpenLinkCommand = new RelayCommand<string>((parameter) => { Process.Start(parameter); });
            SearchCommand = new RelayCommand<string>((parameter) => { int i; if (!parameter.IsNullOrEmpty()) i = 0; });
            OpenUpdaterCommand = new RelayCommand<eUpdateMode>((paramter) =>
            {
                IsAddNewVisible = true;
                EventNotifier.RefreshNewItemWindowCommand.Execute(paramter);
            });

            // EventNotifier.OnAddNewItemClosed += new System.EventHandler((s, e) => //RKD : alternate event call
            EventNotifier.CancelAddNewItemCommand = new RelayCommand<bool>((reloadData) =>
            {
                IsAddNewVisible = false;
                if (reloadData) { DataCache.DBMatrixItems = LoadData(); RefreshData(); }
            });
        }

        private void RefreshConfig()
        {
            //IsSortAscending = true;
            //IsDefaultFirst = true;
            //IsEmptyAllowed = true;
            //IsGroupingAllowed = false;
            //IsAddNewVisible = false;

            // RKD : move to XML
            Dictionary<string, bool> configs = new Dictionary<string, bool>();
            configs.AddToDictionary("IsSortAscending", true);
            configs.AddToDictionary("IsDefaultFirst", true);
            configs.AddToDictionary("IsEmptyAllowed", true);
            configs.AddToDictionary("IsGroupingAllowed", false);
            configs.AddToDictionary("IsAddNewVisible", false);

            configs._ForEach(cfg => this.GetType().GetProperty(cfg.Key).SetValue(this, cfg.Value, null));
        }

        public ObservableCollection<MatrixItem> LoadData()
        {
            ObservableCollection<MatrixItem> allLinks = new ObservableCollection<MatrixItem>();

            // First run
            string path = ConfigurationManager.AppSettings["DBPath"]
                .Replace("~", Assembly.GetExecutingAssembly().Location.GetDirectoryNameFull())
                .AppendPath(ConfigurationManager.AppSettings["MatrixDB"]);

            if (File.Exists(path)) allLinks = DataCache.ReadMatrix();
            else
            {
                Utilities.CreateFile(path);
                DataCache.WriteDefaultMatrix(allLinks);

                return LoadData();
            }

            // allLinks = allLinks.RemoveItems(allLinks._Where(d => d != allLinks.FirstOrDefault())).ToObservableCollection(); // RKD : debugging
            return SortData(allLinks);
        }

        private void RefreshData()
        {
            if (!isLoadComplete) return;
            if (!(DataCache.DBMatrixItems.GetCount() > 0)) return;

            ObservableCollection<MatrixItem> allLinks = new ObservableCollection<MatrixItem>(DataCache.DBMatrixItems);
            allLinks = SortData(DataCache.DBMatrixItems)
                ._Where(lnk => ValidateEmptyRecords(lnk)).ToObservableCollection();
            allLinks.ForEach(lnk => lnk.MatrixGroup = lnk.MatrixGroup.IsNullOrEmpty() ? Resources.Default_MatrixGroup : lnk.MatrixGroup);

            StoreOpenStates(allLinks);
            AllLinks = allLinks;
            RestoreOpenStates(AllLinks);
        }

        private ObservableCollection<MatrixItem> SortData(ObservableCollection<MatrixItem> allLinks)
        {
            allLinks = allLinks
                .OrderByDirection(lnk => lnk.MatrixGroup.IsNullOrEmpty() ? 0 : (lnk.MatrixGroup == DataCache.sMyApps ? 1 : short.MaxValue), IsDefaultFirst)
                .ThenByDirection(lnk => IsGroupingAllowed ? lnk.MatrixGroup : "", IsSortAscending)
                .ThenByDirection(lnk => lnk.Sort, IsSortAscending)
                .ThenByDirection(lnk => lnk.Name, IsSortAscending).ToObservableCollection();

            return allLinks;
        }

        private void StoreOpenStates(ObservableCollection<MatrixItem> allLinks)
        {
            if (AllLinksGrouped.IsLinqIterable(false))
            {
                openStates = new Dictionary<string, bool>();
                AllLinksGrouped.Select(grp => new { Key = grp.MatrixGroup.Coalesce(" "), Value = grp.IsGroupOpen })
                    .ForEach(st => openStates.AddToDictionary(st.Key, st.Value));
            }
        }

        private void RestoreOpenStates(ObservableCollection<MatrixItem> allLinks)
        {
            if (AllLinksGrouped.IsLinqIterable(false))
                AllLinksGrouped.ForEach(grp => grp.IsGroupOpen = (openStates.IsLinqIterable(true) ? openStates.GetValueFromKey(grp.MatrixGroup.Coalesce(" ")) : false));
        }

        private ObservableCollection<MatrixItemGrouped> CreateItemGroups(ObservableCollection<MatrixItem> source)
        {
            ObservableCollection<MatrixItemGrouped> result = new ObservableCollection<MatrixItemGrouped>();
            foreach (string grp in source.Where(itm => ValidateEmptyRecords(itm)).Select(itm => itm.MatrixGroup).Distinct())
            {
                result.Add(new MatrixItemGrouped
                {
                    MatrixGroup = grp,
                    MatrixItems = new ObservableCollection<MatrixItem>(source.Where(itm => itm.MatrixGroup == grp))
                });
            }

            return result;
        }

        private bool ValidateEmptyRecords(MatrixItem itm)
        {
            return IsEmptyAllowed | (itm.MatrixGroup.IsNullOrEmpty() ? !itm.MatrixGroup.IsNullOrEmpty() : itm.MatrixGroup != Resources.Default_MatrixGroup);
        }
        #endregion Methods
    }
}
